`timescale 1ns / 1ps

module mobile_sdram_ctl(SYS_RESET_I, SYS_DATA_I, SYS_DATA_O, SYS_ADDR_I, SYS_WRITE_I, SYS_START_I, SYS_BUSY_O, SDR_CLK_I, SDR_CKE_O, SDR_ADDR_O, SDR_BA_O, SDR_CS_O, SDR_RAS_O, SDR_CAS_O, SDR_WE_O, SDR_DQM_O, SDR_DQ_O, SDR_DQ_I, SDR_OE_O, TEST_BUS_WRITE_I, TEST_BUS_READ_O);
    input SYS_RESET_I;			
    input [31:0] SYS_DATA_I;	
    output [31:0] SYS_DATA_O;	
    input [23:0] SYS_ADDR_I;	
    input SYS_WRITE_I;			
    input SYS_START_I;			
    output SYS_BUSY_O;			
    input SDR_CLK_I;			
    output SDR_CKE_O;			
    output [12:0] SDR_ADDR_O;	
    output [1:0] SDR_BA_O; 		
    output SDR_CS_O;			
    output SDR_RAS_O;			
    output SDR_CAS_O;			
    output SDR_WE_O;			
    output [1:0] SDR_DQM_O;		
    output [31:0] SDR_DQ_O;		
    input [31:0] SDR_DQ_I;		
    output SDR_OE_O;
    input [12:0] TEST_BUS_WRITE_I;			
    output [12:0] TEST_BUS_READ_O;

    
    // not really registers
    reg [12:0] SDR_ADDR_O;
	reg [1:0] SDR_BA_O;
	reg SDR_CS_O; 
	reg SDR_RAS_O;
	reg SDR_CAS_O;
	reg SDR_WE_O;
	reg SYS_BUSY_O;   
    reg [13:0] delay_count;
    reg SDR_CKE_O;
	    
// really registers
   reg [31:0] SYS_DATA_O;
   reg reg_write;	   
   reg [31:0] reg_data_write;
   reg [8:0] reg_col_addr;
   reg [1:0] reg_ba;
   reg [13:0] reg_delay_timer;
   reg [4:0] state;
   reg [4:0] next_state;
   reg [9:0] reg_refresh_timer;
   reg reg_last_start;
   reg reg_go;
   
   
   // debug bus
   reg [12:0] count;
   wire do_count;
   assign TEST_BUS_READ_O = count;
   assign do_count = TEST_BUS_WRITE_I[0]; 
   reg [31:0] divide_count;
  
   
   wire in_delay;
   wire needs_refresh;
   wire reset_refresh;
   
    parameter S_POWER_UP = 5'b00000;
    parameter S_POWER_UP_WAIT = 5'b00001;
    parameter S_PRECHARGE_START = 5'b00010;
    parameter S_PRECHARGE_START_WAIT = 5'b00011;
    parameter S_AUTO_REFRESH_A = 5'b00100;
    parameter S_AUTO_REFRESH_A_WAIT = 5'b00101;
    parameter S_AUTO_REFRESH_B = 5'b00110;
    parameter S_AUTO_REFRESH_B_WAIT = 5'b00111;
    parameter S_MODE_REG_A = 5'b01000;
    parameter S_MODE_REG_A_WAIT = 5'b01001;
    parameter S_MODE_REG_B = 5'b01010;
    parameter S_MODE_REG_B_WAIT = 5'b01011;
    parameter S_PRECHARGE = 5'b01100;
    parameter S_PRECHARGE_WAIT = 5'b01101;
    parameter S_AUTO_REFRESH = 5'b01110;
    parameter S_IDLE = 5'b01111;
    parameter S_ACTIVE = 5'b10000;
    parameter S_ACTIVE_WAIT = 5'b10001;
    parameter S_READ = 5'b10011;
    parameter S_WRITE = 5'b10100;
    parameter S_WRITE_DATA = 5'b10101;
    parameter S_WRITE_WAIT = 5'b10110;
    parameter S_READ_DATA_OUT = 5'b10111;
    parameter S_READ_CAS_WAIT = 5'b11000;
    parameter S_AUTO_REFRESH_WAIT = 5'b11001;
    parameter S_AUTO_REFRESH_CHECK = 5'b11010;
    parameter S_COUNT = 5'b11011;

       
   	assign in_delay = ~(reg_delay_timer==0 );
   	assign needs_refresh = (reg_refresh_timer==0);
   	assign reset_refresh = (state == S_AUTO_REFRESH);
   	
	assign SDR_DQM_O = 2'b0;
	assign SDR_DQ_O = reg_data_write;
	assign SDR_OE_O = ~reg_write;
	
	
	always @(posedge SDR_CLK_I or posedge SYS_RESET_I)
	begin
		if (SYS_RESET_I)
		begin
			count <= 0;
			divide_count <= 0;
		end
		else
		begin
		
			if (do_count)
			begin
				count <= count+1;
			end
			else
			begin
				count <= count;
			end
			divide_count <= divide_count + 1;
			
/*			//count <= SYS_DATA_I[31:19];
			if (divide_count == 100000000)
			begin
				divide_count <= 0;
				count <= count + 1;
			end
			else
			begin				
				divide_count <= divide_count + 1;
				count <= count;
			end
			*/
		end
	end
	
	// synthesis attribute init state : signal is "S_POWER_UP";
    
   always @(negedge SDR_CLK_I, posedge SYS_RESET_I)
   begin
   		if (SYS_RESET_I)
   		begin
   			reg_delay_timer <= 13'b0;
   			state <= S_POWER_UP;
   			
   			reg_write <= 0;	   
   			reg_data_write <= 0;
   			reg_col_addr <= 9'b0;
   			reg_ba <= 2'b0;
   			
   			reg_refresh_timer <= 0;
   			
   			SYS_DATA_O <= 20;
   			reg_last_start <= 0;
   			reg_go <= 0;
   		end
   		else
   		begin
   			reg_last_start <= SYS_START_I;
   			
   			if (~reg_last_start & SYS_START_I)
   			begin
   				reg_go <= 1;
   			end
   			else
   			begin
   				if (state == S_ACTIVE)
   				begin
   					reg_go <= 0;
   				end
   				else
   				begin
   					reg_go <= reg_go;
   				end
   			end
   			
   			if (reset_refresh)
   			begin
   				reg_refresh_timer <= 750;
   			end
   			else
   			begin   				
   				if (~needs_refresh)
   				begin
					reg_refresh_timer <= reg_refresh_timer - 1;   			
				end
				else
				begin
					reg_refresh_timer <= 700;
				end
			end
			
   			state <= next_state;
   			   			
   			if (in_delay)
   			begin
   				reg_delay_timer <= reg_delay_timer - 1;
   			end
   			else
   			begin
   				reg_delay_timer <= delay_count;
   			end	
   			
   			if (next_state == S_ACTIVE)
   			begin
   				reg_data_write <= SYS_DATA_I;
   				reg_col_addr <= SYS_ADDR_I[21:13];
   				reg_ba <= SYS_ADDR_I[23:22];
   				reg_write <= SYS_WRITE_I;
   			end
   			else
   			begin
   				reg_data_write <= reg_data_write;
   				reg_col_addr <= reg_col_addr;
   				reg_ba <= reg_ba;
   				reg_write <= (reg_write && (next_state != S_IDLE));   			   				
   			end
   			
   			if (state == S_READ_CAS_WAIT && next_state == S_IDLE)
   			begin
   				SYS_DATA_O <= SDR_DQ_I;
   				//SYS_DATA_O <= {reg_write,reg_ba,1'b1,reg_col_addr,3'b101,reg_data_write[15:0]};
   			end
   			else
   			begin
   				if(state == S_WRITE) 
   				begin
   					SYS_DATA_O <= reg_data_write;
   					//SYS_DATA_O <= {reg_write,reg_ba,1'b1,reg_col_addr,3'b111,reg_data_write[15:0]};
   					//SYS_DATA_O <= 10;
   				end
   				else
   				begin
   					SYS_DATA_O <= SYS_DATA_O;
   				end
   			end
   					
   		end
   end
   
   
   always @(*)
   begin
   	case (state)
   		S_POWER_UP : begin
   			SDR_CKE_O <= 0;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 1;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 10100;
 			SYS_BUSY_O<= 1;	
   			
			next_state <= S_POWER_UP_WAIT; 
		  end
 		S_POWER_UP_WAIT : begin 			
   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;	
   			
   			if (in_delay)
   			begin
				next_state <= S_POWER_UP_WAIT; 
   			end
   			else
   			begin
				next_state <= S_PRECHARGE_START; 
			end
		  end
		  
 		S_PRECHARGE_START : begin
 		
   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0010000000000;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 0;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 0;
   			 
   			delay_count <= 200;
 			SYS_BUSY_O<= 1;	
 		
			next_state <= S_PRECHARGE_START_WAIT; 
		  end
			
 		S_PRECHARGE_START_WAIT : begin 
 		
   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;	
   			
   			if (in_delay)
   			begin
				next_state <= S_PRECHARGE_START_WAIT; 
   			end
   			else
   			begin
				next_state <= S_AUTO_REFRESH_A; 
			end
		  end
			
 		S_AUTO_REFRESH_A : begin 

   			SDR_CKE_O <= 1;   		
 			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 0;
   			SDR_CAS_O <= 0;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 200;
 			SYS_BUSY_O<= 1;	
 		
			next_state <= S_AUTO_REFRESH_A_WAIT; 
		  end
			
 		S_AUTO_REFRESH_A_WAIT : begin 

   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;	
   			
   			if (in_delay)
   			begin
				next_state <= S_AUTO_REFRESH_A_WAIT; 
   			end
   			else
   			begin
				next_state <= S_AUTO_REFRESH_B; 
			end
		  end
 					
 		S_AUTO_REFRESH_B : begin 
 		
   			SDR_CKE_O <= 1;   		
 			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 0;
   			SDR_CAS_O <= 0;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 200;
 			SYS_BUSY_O<= 1;	
 		
			next_state <= S_AUTO_REFRESH_B_WAIT; 
		  end
			
 		S_AUTO_REFRESH_B_WAIT : begin 
 		
   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;	
   			
   			if (in_delay)
   			begin
				next_state <= S_AUTO_REFRESH_B_WAIT; 
   			end
   			else
   			begin
				next_state <= S_MODE_REG_A; 
			end
		  end
			
 		S_MODE_REG_A : begin 
 		
   			SDR_CKE_O <= 1;   		
 			SDR_ADDR_O <= 13'b000_0_00_011_0_000; //CAS latency = 3
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 0;
   			SDR_CAS_O <= 0;
   			SDR_WE_O <= 0;
   			 
   			delay_count <= 200;
 			SYS_BUSY_O<= 1;	
 		
			next_state <= S_MODE_REG_A_WAIT; 
		  end
			
 		S_MODE_REG_A_WAIT : begin 
 		
   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;	
   			
   			if (in_delay)
   			begin
				next_state <= S_MODE_REG_A_WAIT; 
   			end
   			else
   			begin
				next_state <= S_MODE_REG_B; 
			end
		  end
			
 		S_MODE_REG_B : begin 
 		 		
   			SDR_CKE_O <= 1;   		
 			SDR_ADDR_O <= 13'b000000_00_00_000;
   			SDR_BA_O <= 2'b10;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 0;
   			SDR_CAS_O <= 0;
   			SDR_WE_O <= 0;
   			 
   			delay_count <= 200;
 			SYS_BUSY_O<= 1;	
 		
			next_state <= S_MODE_REG_B_WAIT; 
		  end
			
 		S_MODE_REG_B_WAIT : begin 
 		
   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;	
   			
   			if (in_delay)
   			begin
				next_state <= S_MODE_REG_B_WAIT; 
   			end
   			else
   			begin
				next_state <= S_PRECHARGE; 
			end
		  end
			
 		S_PRECHARGE : begin 

   			SDR_CKE_O <= 1;   		
  			SDR_ADDR_O <= 13'b0010000000000;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 0;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 0;
   			 
   			delay_count <= 2;
 			SYS_BUSY_O<= 1;	

			next_state <= S_PRECHARGE_WAIT;
		  end

		S_PRECHARGE_WAIT : begin 
 		
   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;	
   			
   			if (in_delay)
   			begin
				next_state <= S_PRECHARGE_WAIT; 
   			end
   			else
   			begin
				next_state <= S_AUTO_REFRESH; 
			end			
		  end
									
 		S_AUTO_REFRESH : begin 
 		
   			SDR_CKE_O <= 1;   		
 			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 0;
   			SDR_CAS_O <= 0;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8;
 			SYS_BUSY_O<= 0;	
 		
			next_state <= S_AUTO_REFRESH_WAIT; 			
		  end
			
		S_AUTO_REFRESH_WAIT : begin 
 		
   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;	
   			
   			if (in_delay)
   			begin
				next_state <= S_AUTO_REFRESH_WAIT; 
   			end
   			else
   			begin
				next_state <= S_IDLE; 
			end			
		  end		  
		  
		  			
 		S_IDLE : begin 
 		
   			SDR_CKE_O <= 1;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= reg_go;	
 		
 			if (needs_refresh)
 			begin
 				next_state <= S_AUTO_REFRESH;
 			end
 			else
 			begin
	 			if (reg_go | (~reg_last_start & SYS_START_I))
	 			begin
	 				next_state <= S_ACTIVE;
	 			end
	 			else
	 			begin
	 				next_state <= S_IDLE;
	 			end 			
	 		end
		  end
			
 		S_ACTIVE : begin 

   			SDR_CKE_O <= 1;   		
 			SDR_ADDR_O <= SYS_ADDR_I[12:0];
   			SDR_BA_O <= SYS_ADDR_I[23:21];
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 0;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 2;
 			SYS_BUSY_O<= 1;	
 			
 			next_state <= S_ACTIVE_WAIT;
		  end
 					
 		S_ACTIVE_WAIT : begin 

   			SDR_CKE_O <= 1;   		
 			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= reg_ba;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O <= 1;	
 			
 			
 			if (in_delay)
   			begin
				next_state <= S_ACTIVE_WAIT; 
   			end
   			else
   			begin
				if (reg_write)
   				begin
					next_state <= S_WRITE; 
				end
				else
				begin
					next_state <= S_READ; 
				end
			end
		  end
						 				
 		S_WRITE : begin 
 		
   			SDR_CKE_O <= 1;   		
 		 	SDR_ADDR_O <= {4'b0010,reg_col_addr};
   			SDR_BA_O <= reg_ba;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 0;
   			SDR_WE_O <= 0;
   			 
   			delay_count <= 10;
 			SYS_BUSY_O <= 1;	
 			
			next_state <= S_WRITE_WAIT; 
		  end
		
		S_WRITE_DATA : begin 

   			SDR_CKE_O <= 1;   		
		 	SDR_ADDR_O <= {4'b0010,reg_col_addr};
   			SDR_BA_O <= reg_ba;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 0;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 2;
 			SYS_BUSY_O <= 1;	
 		
			next_state <= S_WRITE_WAIT; 
		  end
							
		S_WRITE_WAIT : begin 

   			SDR_CKE_O <= 1;   		
		   	SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;	
   			
   			if (in_delay)
   			begin
				next_state <= S_WRITE_WAIT; 
   			end
   			else
   			begin
				next_state <= S_IDLE; 
			end
		  end
				
 		S_READ : begin 
 			
   			SDR_CKE_O <= 1;   		
		 	SDR_ADDR_O <= {4'b0010,reg_col_addr};
   			SDR_BA_O <= reg_ba;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 0;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 2;
 			SYS_BUSY_O<= 1;	
 			
			next_state <= S_READ_CAS_WAIT;
		  end

		S_READ_CAS_WAIT : begin 
 	
   			SDR_CKE_O <= 1;   		
		 	SDR_ADDR_O <= {4'b0010,reg_col_addr};
   			SDR_BA_O <= reg_ba;
   			SDR_CS_O <= 0;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 8'b0;
 			SYS_BUSY_O<= 1;
 			
   			if (in_delay)
   			begin
				next_state <= S_READ_CAS_WAIT; 
   			end
   			else
   			begin
				next_state <= S_IDLE; 
			end
		  end

		  default : begin 
 	
			SDR_CKE_O <= 0;   		
   			SDR_ADDR_O <= 13'b0;
   			SDR_BA_O <= 2'b0;
   			SDR_CS_O <= 1;
   			SDR_RAS_O <= 1;
   			SDR_CAS_O <= 1;
   			SDR_WE_O <= 1;
   			 
   			delay_count <= 10100;
 			SYS_BUSY_O<= 1;	
   			
			next_state <= S_POWER_UP; 		  
		end
	endcase
   
   end
	
endmodule
